<template>
  <Mapbox :mapOnLoadCB="mapOnLoadCB"></Mapbox>
</template>

<script>
import {ref} from "vue"
import Mapbox from "@/components/common/Mapbox.component.vue"
import MapboxCommonService from "@/service/map/MapboxCommonService"
import MapboxAssetsService from "@/service/http/MapboxAssets.service"

export default {
  name: "AddAnimateBuilding.component",
  components: {Mapbox},
  setup() {

    let mapStore = ref({});
    let mapOnLoadCB = async (map) => {
      mapStore.value = map;

      let {data} = await MapboxAssetsService.queryStyle("style-dancing-buildings.json")

      // 将运行的样式API（styling API）与网络音频API（Web Audio API）关联，创建一幅3D建筑物可随周边环境的节奏起舞的地图。
      map.addControl(new mapboxgl.FullscreenControl());
      map.setStyle(data)
      MapboxCommonService.setCZBP(map, [-74.00649562332922, 40.70811328605049], 15, 0, 55)

      setTimeout(() => {
        let bins = 16;
        let maxHeight = 200;
        let binWidth = maxHeight / bins;

        // Divide the buildings into 16 bins based on their true height, using a layer filter.
        for (let i = 0; i < bins; i++) {
          map.addLayer({
            'id': '3d-buildings-' + i,
            'source': 'composite',
            'source-layer': 'building',
            'filter': ['all', ['==', 'extrude', 'true'], ['>', 'height', i * binWidth], ['<=', 'height', (i + 1) * binWidth]],
            'type': 'fill-extrusion',
            'minzoom': 15,
            'paint': {
              'fill-extrusion-color': '#aaa',
              'fill-extrusion-height-transition': {
                duration: 0,
                delay: 0
              },
              'fill-extrusion-opacity': .6
            }
          });
        }

        // Older browsers might not implement mediaDevices at all, so we set an empty object first
        if (navigator.mediaDevices === undefined) {
          navigator.mediaDevices = {};
        }

        // Some browsers partially implement mediaDevices. We can't just assign an object
        // with getUserMedia as it would overwrite existing properties.
        // Here, we will just add the getUserMedia property if it's missing.
        if (navigator.mediaDevices.getUserMedia === undefined) {
          navigator.mediaDevices.getUserMedia = (constraints) => {

            // First get ahold of the legacy getUserMedia, if present
            let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

            // Some browsers just don't implement it - return a rejected promise with an error
            // to keep a consistent interface
            if (!getUserMedia) {
              return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
            }

            // Otherwise, wrap the call to the old navigator.getUserMedia with a Promise
            return new Promise((resolve, reject) => {
              getUserMedia.call(navigator, constraints, resolve, reject);
            });
          };
        }

        navigator.mediaDevices.getUserMedia({audio: true}).then((stream) => {
          // Set up a Web Audio AudioContext and AnalyzerNode, configured to return the
          // same number of bins of audio frequency data.
          let audioCtx = new (window.AudioContext || window.webkitAudioContext)();

          let analyser = audioCtx.createAnalyser();
          analyser.minDecibels = -90;
          analyser.maxDecibels = -10;
          analyser.smoothingTimeConstant = 0.85;

          let source = audioCtx.createMediaStreamSource(stream);
          source.connect(analyser);

          analyser.fftSize = bins * 2;

          let dataArray = new Uint8Array(bins);

          const draw = (now) => {
            analyser.getByteFrequencyData(dataArray);

            // Use that data to drive updates to the fill-extrusion-height property.
            let avg = 0;
            for (let i = 0; i < bins; i++) {
              avg += dataArray[i];
              map.setPaintProperty('3d-buildings-' + i, 'fill-extrusion-height', 10 + 4 * i + dataArray[i]);
            }
            avg /= bins;

            // Animate the map bearing and light color over time, and make the light more
            // intense when the audio is louder.
            map.setBearing(now / 500);
            map.setLight({
              color: "hsl(" + now / 100 % 360 + "," + Math.min(50 + avg / 4, 100) + "%,50%)",
              intensity: Math.min(1, avg / 256 * 10)
            });

            requestAnimationFrame(draw);
          }

          requestAnimationFrame(draw);
        }).catch((err) => {
          console.log('The following gUM error occured: ' + err);
        });
      }, 1000)
    };

    return {
      mapOnLoadCB
    }
  }
}
</script>

<style scoped>

</style>